import { exec } from "node:child_process";
import { promises as fs } from "node:fs";
import path from "node:path";
import { rimraf } from "rimraf";

import { registry } from "../registry/index.js";

type RegistryFile = {
  path: string;
  type: string;
  target?: string;
};

type RegistryItem = {
  name: string;
  description?: string;
  type: string;
  registryDependencies?: string[];
  files?: RegistryFile[];
  categories?: string[];
  meta?: Record<string, unknown>;
};

async function buildRegistryIndex() {
  let index = `/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"

// biome-ignore lint/suspicious/noExplicitAny: Auto-generated registry index with dynamic component types
export const Index: Record<string, any> = {`;
  for (const item of registry.items) {
    const resolveFiles = item.files?.map(
      (file: RegistryFile) => `registry/default/${file.path}`,
    );
    if (!resolveFiles) {
      continue;
    }

    const componentPath = item.files?.[0]?.path
      ? `@/registry/default/${item.files[0].path}`
      : "";

    index += `
  "${item.name}": {
    name: "${item.name}",
    description: "${item.description ?? ""}",
    type: "${item.type}",
    registryDependencies: ${JSON.stringify(item.registryDependencies)},
    files: [${item.files?.map((file: RegistryFile) => {
      const filePath = `registry/default/${typeof file === "string" ? file : file.path}`;
      const resolvedFilePath = path.resolve(filePath);
      return typeof file === "string"
        ? `"${resolvedFilePath}"`
        : `{
      path: "${filePath}",
      type: "${file.type}",
      target: "${file.target ?? ""}"
    }`;
    })}],
    component: ${
      componentPath
        ? `React.lazy(async () => {
      const mod = await import("${componentPath}")
      const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') || item.name
      return { default: mod.default || mod[exportName] }
    })`
        : "null"
    },
    categories: ${JSON.stringify(item.categories)},
    meta: ${JSON.stringify(item.meta)},
  },`;
  }

  index += `
  }`;

  console.log(`#️⃣  ${Object.keys(registry.items).length} items found`);

  // Write style index.
  rimraf.sync(path.join(process.cwd(), "registry/__index__.tsx"));
  await fs.writeFile(path.join(process.cwd(), "registry/__index__.tsx"), index);
}

async function buildRegistryJsonFile() {
  // 1. Fix the path for registry items.
  const fixedRegistry = {
    ...registry,
    items: registry.items.map((item: RegistryItem) => {
      const files = item.files?.map((file: RegistryFile) => {
        return {
          ...file,
          path: `registry/default/${file.path}`,
        };
      });

      return {
        ...item,
        files,
      };
    }),
  };

  // 2. Write the content of the registry to `registry.json`
  rimraf.sync(path.join(process.cwd(), "registry.json"));
  await fs.writeFile(
    path.join(process.cwd(), "registry.json"),
    JSON.stringify(fixedRegistry, null, 2),
  );

  // 3. Copy the registry.json to the public/r directory.
  await fs.cp(
    path.join(process.cwd(), "registry.json"),
    path.join(process.cwd(), "public/r/registry.json"),
    { recursive: true },
  );
}

async function buildRegistry() {
  return new Promise((resolve, reject) => {
    const process = exec(
      "bunx --bun shadcn build registry.json --output public/r",
    );

    process.on("exit", (code) => {
      if (code === 0) {
        resolve(undefined);
      } else {
        reject(new Error(`Process exited with code ${code}`));
      }
    });
  });
}

try {
  console.log("🗂️ Building registry/__index__.tsx...");
  await buildRegistryIndex();

  console.log("💅 Building registry.json...");
  await buildRegistryJsonFile();

  console.log("🏗️ Building registry...");
  await buildRegistry();

  console.log("✅ Registry build completed successfully!");
} catch (error) {
  console.error(error);
  process.exit(1);
}
